#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "processo.h"
#include "in_out.h"
#include <string.h>

/*
	Cria um processo
*/
processo_simulado *criar_processo (){
	processo_simulado *p = malloc(sizeof(processo_simulado));
	p->programa = alocar_vetor(50);
	return p;	
}

/*
	Inicia o processo, le o arquivo de intruções e inicializa suas variaveis.
*/
processo_simulado *iniciar_processo(char *nome_arquivo, int pid){
	
	FILE *arquivo;
	char str[6], str2[6];
	int r, x = 0;
	processo_simulado *ps = criar_processo();
	
	/*A prioridade será atribuida de forma aleatoria*/
	srand(time(NULL));
	
	/*Abre o arquivo de instruções*/
	arquivo = fopen(nome_arquivo,"r");
	if(!(arquivo)){
		printf("Erro ao abrir o arquivo");
		exit (EXIT_FAILURE);	
	}
	
	/*Faz a leitura do arquivo de instrucoes*/
	while(!feof(arquivo)){
		/*T marca o final da lista de comandos*/
		fscanf(arquivo, "%s", str);
		if(strcmp(str,"E") || strcmp(str,"e")){	
			strcpy(ps->programa[x].comando, str);
			fscanf(arquivo, "%s", str2);
			strcpy(ps->programa[x].instrucao,str2); 
			x++;
		}else break;
	}
	/*Fecha o arquivo*/
	fclose(arquivo);
	
	/*Guarda o tamanho do processo. Ou seja, o numero de instruçoes que ele possui.*/
	ps->id=id_processos++;
	ps->tamanho = x;
	ps->contador_programa = 0;
	ps->variavel_local = 0;		    
	ps->tempo_usado = contador_global;			
	ps->estado = 1;				
	//ps->pid = pid;			
	ps->prioridade = rand()%10;
	/*Ponteiro aponta para a estrutura.*/
	return ps;
}

/*
	Inicia o processo, le o arquivo de intruções e inicializa suas variaveis.
*/
processo_simulado *fork_simulado(processo_simulado *cpu){
	
	processo_simulado *ps = criar_processo();
	
	/*Guarda o tamanho do processo. Ou seja, o numero de instruçoes que ele possui.*/
	ps->id=id_processos++;
	ps->tamanho = cpu->tamanho;
	ps->contador_programa = cpu->contador_programa;
	ps->variavel_local = cpu->variavel_local;		    
	ps->tempo_usado = contador_global;			
	ps->estado = 1;				
	ps->pid = cpu->id;			
	ps->prioridade = cpu->prioridade;
	ps->programa = cpu->programa;
	
	/*Ponteiro aponta para a estrutura.*/
	return ps;
}

/*
	Inicia o processo, le o arquivo de intruções e inicializa suas variaveis.
*/
instrucoes *troca_imagem(char *nome_arquivo){
	
	instrucoes *programa = alocar_vetor(100);
	FILE *arquivo;
	char str[6], str2[6];
	int r, x = 0;
	
	/*Abre o arquivo de instruções*/
	arquivo = fopen(nome_arquivo,"r");
	if(!(arquivo)){
		printf("Erro ao abrir o arquivo");
		exit (EXIT_FAILURE);	
	}
	
	/*Faz a leitura do arquivo de instrucoes*/
	while(!feof(arquivo)){
		/*T marca o final da lista de comandos*/
		fscanf(arquivo, "%s", str);
		if(strcmp(str,"E") || strcmp(str,"e")){	
			strcpy(programa[x].comando, str);
			fscanf(arquivo, "%s", str2);
			strcpy(programa[x].instrucao,str2); 
			x++;
		}else break;
	}
	/*Fecha o arquivo*/
	fclose(arquivo);
	
	/*Ponteiro aponta para a estrutura.*/
	return programa;
}

/*Verifica a instrução que estiver na cpu*/
void executa_instrucao(){
	
	/*Se a cpu estiver vazia, não existe nada pra executar.*/
	if (cpu == NULL) return;
	
	char *comando = cpu->programa[cpu->contador_programa].comando;
	char *instrucao = cpu->programa[cpu->contador_programa].instrucao;
	
	printf("Comando %s\n",comando);
	
	if(!strcmp(comando,"s") || !strcmp(comando,"S")){
		/*Atribui um valor à variavel local*/	
		cpu->variavel_local = atoi(instrucao);
		cpu->contador_programa++;
		return;
	
	}else if(!strcmp(comando,"a") || !strcmp(comando,"A")){
		/*Soma um valor à variavel local*/	
		cpu->variavel_local = cpu->variavel_local + atoi(instrucao);
		cpu->contador_programa++;
		return;
	
	}else if(!strcmp(comando,"d") || !strcmp(comando,"D")){
		/*Atribui um valor à variavel local*/	
		cpu->variavel_local = cpu->variavel_local - atoi(instrucao);
		cpu->contador_programa++;
		return;
	
	}else if(!strcmp(comando,"b") || !strcmp(comando,"B")){
		
		/*Bloqueia o processo simulado. Marca o processo da cpu como bloqueado e o coloca na lista de bloqueados*/
		bloquear_processo();		
		
		/*Nesse caso, deve-se escalonar. Para isso, vai depender do escalonador usado*/
		escalonar();
		return;
	
	}else if(!strcmp(comando,"e") || !strcmp(comando,"E")){
		
		/*Finaliza o processo*/
		encerrar_processo();
		
		/*Escalona.*/
		escalonar();
		return;
	
	}else if(!strcmp(comando,"f") || !strcmp(comando,"F")){
		/*Simula o fork(). Cria um processo filho, igual ao pai, com o contador de programa identico ao do pai.*/
		processo_simulado *p = criar_processo();
		
		/*Cria o processo filho. Que é uma copia EXATA do processo pai.*/
		p = fork_simulado(cpu);
		
		/*tendo criado o processo, ele é adicionado na tabela pcb e na lista de processos prontos.*/
		adiciona_processo_pcb(p);
		adiciona_processo_lista_prontos(p);
		return;
	
	}else if(!strcmp(comando,"r") || !strcmp(comando,"R")){
		
		/*O antigo programa é apagado.*/
		liberar_vetor(cpu->programa);
		
		/*O novo programa é colocado em seu lugar. Contador do programa é resetado.*/
		cpu->programa = troca_imagem(instrucao);
		cpu->contador_programa = 0;
		
		return;
	}
}

void bloquear_processo(){
	
	if(cpu != NULL){
		/*Deve-se fazer com o processo na cpu, va para a lista de processos bloqueados*/	
		
		/*Estado do processo é marcado como bloqueado.*/
		cpu->estado = 2;
		
		/*Adiciona o processo na lista de processos bloqueados*/
		adiciona_processo_lista_bloqueados(cpu);
		
		/*Remove o processo da tabela de processos prontos*/
		remover_processo(cpu->id,2);
		
		cpu = NULL;
	}else {
		printf("Não há processos executando no momento.");
	}	
}


void desbloquear_processo(){
	
	/*Desbloqua o primeiro processo na lista de processos bloqueados*/	
	
	if(qtd_processos_bloqueados > 0){
		
		adiciona_processo_lista_prontos(processos_bloqueados[0]);
		remover_processo(processos_bloqueados[0]->id,1);
	}else{ 
		printf("Lista vazia!\n");
	}	
	
}


void encerrar_processo(){
	
	/*
	  Termina o processo. Marca-o como terminado. 
	  Remove-o da tabela de processos e dos estados prontos e bloqueados.
	*/
	remover_tabela_pcb(cpu->id);
	remover_processo(cpu->id,2);
}

/*Funçao auxiliar*/
void imprime_instrucoes(){
	
	int x;
	for(x=0; x< cpu->tamanho; x++){
		printf("%s - %s\n",cpu->programa[x].comando,cpu->programa[x].instrucao);
	}
	getchar();
}

void escalonar(){
	/*Vai depender EXCLUSIVAMENTE do escalonador escolhido*/
	if(qtd_processos_prontos == 0){
		printf("Não existem processos prontos!\n");
		return;
	}
	
	switch(tipo_escalonador){
		case 1:
			/*Prioridade.*/
			//printf("E la vamos nos\n");
			qsort(processos_prontos,(size_t) qtd_processos_prontos, sizeof(processo_simulado), (void *)comparaPrioridade);
			cpu = processos_prontos[0];
			break;
		case 2:
			/*Menor tempo.*/
			qsort(processos_prontos,(size_t) qtd_processos_prontos, sizeof(processo_simulado), (void *)comparaTempo);
			cpu = processos_prontos[0];
			break;
		case 3:
			/*Loteria.*/
			cpu = processos_prontos[escolherAleatorio()];
			break;	
	}
}

void processo_reporter(){
	printf("**********************************************************\n");
	printf("Estado do sistema:\n");
	printf("**********************************************************\n");	
	printf("TEMPO ATUAL %d\n", contador_global);
	
	if(cpu == NULL){
		printf("PROCESSO EXECUTANDO\n id:	pid:	Prioridade:	   Tempo Ini:	CPU\n");
		printf("NÃO EXISTE UM PROCESSO EXECUTANDO\n");
	}else{
		printf("PROCESSO EXECUTANDO\n id:	pid:	Prioridade:	   Tempo Ini:	CPU\n");
		printf("%d	  %d	%d	  %d	%d\n",cpu->id,cpu->pid,cpu->prioridade,cpu->tempo_usado,cpu->contador_programa);
	}
	
	printf("**********************************************************\n");	
	printf("PROCESSOS PRONTOS\n id:	pid:	Prioridade:	   Tempo Ini:	CPU\n");
	imprime_lista_processos(processos_prontos,2);
	printf("**********************************************************\n");	
	printf("PROCESSOS BLOQUEADOS\n id:	pid:	Prioridade:	   Tempo Ini:	CPU\n");
	imprime_lista_processos(processos_bloqueados,3);
	printf("**********************************************************\n");	
	getchar();
}

/**
 * Retorna
 * 		1: tempo de p1 > tempo de p2
 * 		0: tempo de p1 = tempo de p2
 * 		-1: tempo de p2 > tempo de p1
 */ 
int comparaTempo(processo_simulado *p1, processo_simulado *p2){
	if(p1->tempo_usado > p2->tempo_usado) return 1;
	else if(p1->tempo_usado == p2->tempo_usado) return 0;
	else return -1;
}

/**
 * Retorna
 * 		1: prioridade de p1 > prioridade de p2
 * 		0: prioridade de p1 = prioridade de p2
 * 		-1: prioridade de p2 > prioridade de p1
 */  
int comparaPrioridade(processo_simulado *p1, processo_simulado *p2){
	if(p1->prioridade > p2->prioridade) return 1;
	else if(p1->prioridade == p2->prioridade) return 0;
	else return -1;
}

int escolherAleatorio(){
	srand(time(NULL)); //a cada execução gera novos valores
	
	return rand() % qtd_processos_prontos; //escolhe um número aleatório entre 0 e qtd_processos_prontos
}
